package com.boot.project.admin.shiro.realm;

import com.boot.project.core.constant.SystemConstant;
import com.boot.project.core.model.result.ProfileResult;
import com.boot.project.core.model.system.entity.Resource;
import com.boot.project.core.model.system.entity.User;
import com.boot.project.core.util.RequestUtils;
import com.boot.project.module.system.mapper.ResourceMapper;
import com.boot.project.module.system.mapper.UserMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.util.*;

@Slf4j
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ResourceMapper resourceMapper;

    /**
     * 鉴权
     * @param principals 用户的身份信息
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 1.获取安全数据
        ProfileResult result = (ProfileResult) principals.getPrimaryPrincipal();
        // 2.获取权限信息
        Set<String> resources = result.getResult().getButtons();
        // 3.构造安全数据并返回
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(resources);
        return info;
    }

    /**
     * 用户认证
     * @param token 认证信息
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.info("-----------认证开始");
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;

        String username = upToken.getUsername();
        String password = new String(upToken.getPassword());

        Map<String, String> logMap = new HashMap<>();
        User dbUser = userMapper.findByUsername(username);

        if (dbUser == null) {
            logMap.put("success", "false");
            logMap.put("reason", "用户名不存在");
            this.doLog(username, logMap);
            throw new UnknownAccountException("用户名不存在");
        }

        logMap.put("success", "true");
        this.doLog(username, logMap);

        List<Resource> list = resourceMapper.findByRoleId(dbUser.getRoleId());
        ProfileResult profileResult = this.buildProfile(dbUser, SystemConstant.DEFAULT_PASSWORD.equals(password), list);

        return new SimpleAuthenticationInfo(profileResult, dbUser.getPassword(),
                ByteSource.Util.bytes(dbUser.getSalt()), super.getName());
    }

    /**
     * 根据用户对象和所拥有的全新信息构建其完整的用户信息
     * @param user 用户对象
     * @param firstLogin 是否首次登录
     * @param list 用户对应的权限信息
     * @return
     */
    private ProfileResult buildProfile(User user, Boolean firstLogin, List<Resource> list) {
        if (CollectionUtils.isEmpty(list)) {
            return new ProfileResult(user, firstLogin, new ArrayList<>());
        }
        return new ProfileResult(user, firstLogin, list);
    }

    /**
     * 日志打印
     * @param username 用户名
     * @param logMap 认证结果
     * @throws Exception
     */
    private void doLog(String username, Map<String, String> logMap) {

        String result = logMap.get("success");
        String reason = logMap.get("reason");

        if (Boolean.parseBoolean(result)) {
            log.info("来自ip为[{}]的登录，登录账号[{}]，登录结果：[{}]", RequestUtils.getIp(), username, result);
        } else {
            log.info("来自ip为[{}]的登录，登录账号[{}]，登录结果：[{}]，原因：[{}]", RequestUtils.getIp(),
                    username, result, reason);
        }
    }
}